KURZUS: Programozás alapjai

MODUL: I. modul

2. lecke: Alapismeretek 1/2

| Az első lecke során a feladatok megoldását csak adatszerkezeti táblázatokkal és folyamatábrákkal tudtuk megfogalmazni. Ennek a leckének a során viszont elkészítjük első C programunkat, és elkezdjük megvizsgálni, milyen szintaktikai szabályok betartásával írhatjuk le gondolatainkat programnyelvi eszközök segítségével. Megvizsgáljuk, hogy a begépelt programszövegből milyen lépések eredményeképpen áll elő egy futtatható program.
A lecke végére:
Megtudja a hallgató, hogy miért használunk programnyelveket. Milyen főbb tulajdonságokkal rendelkezik a C programnyelv. Megtanulja a különbséget fordítóprogramok és értelmezők között. Megtanulja, hogy milyen résztevékenységek vezetnek el a futtatható program elkészültéig. Képes lesz a szabványos be- és kimenet alapszintű használatára. Az előfeldolgozó legfontosabb szolgáltatásainak igénybe vételére. Egyszerű függvények készítésére, változók, konstansok definiálására. Megismerkedik a legfontosabb típusokkal. Ciklusok, feltételek főbb változatainak használatára.
A lecke kulcsfogalmai: programnyelv, forráskód, interpreter, compiler, megjegyzés, előfeldolgozó, fordító, kapcsoló-szerkesztő, direktíva, fehér karakter, fejfájl, escape szekvencia, futásidejű könyvtár, deklaráció, definíció, prototípus, összetett utasítás, bal- és jobbérték, formátumspecifikáció, szimbolikus állandó, verem.
A C programnyelvről röviden
Minden számítógépnek van egy saját utasításkészlete, és csak az ezzel megadott utasítás sorozatok végrehajtására képes. Ezt hívják gépi kódnak, amivel teljes körűen ki lehet ugyan aknázni egy számítógép képességeit, ám cserébe annak legmélyebb ismeretét és óriási szakértelmet igényel, rendkívül lassú lesz a program fejlesztése, az elkészült programokat nem lehet más hardveren használni, és számos más hátránya is van a használatának. Ezeket a gondokat csak kevéssé enyhíti az Assembly, pl. azzal, hogy a gépi utasításkódok helyett azok könnyebben megjegyezhető, rövid szöveges elnevezéseit (mnemonik) lehet használni. Azért alkalmazunk különféle programnyelveket, hogy az emberi gondolkodásmódra jobban emlékeztető módon, gyorsabban, tömörebben, kevesebb hibával, korábban már létrehozott megoldásokat újrahasználva stb. fogalmazhassuk meg szándékunkat, majd a programnyelv szabályai szerint elkészített ún. forráskódot (forrásszöveget, forrásprogramot) alakíttatjuk át a számítógép által közvetlenül is végrehajtható programmá.
Ez az átalakítás alapvetően kétféle módon tud történni: az ún. értelmezők (interpreter) sorról-sorra, kis egységekben előrehaladva értelmezik és hajtatják végre a számítógéppel az utasításainkat, aminek egyik előnye, hogy a program a forrásszöveg elkészítését követően azonnal használatba vehető. A hátránya azonban a lassú végrehajtás, mert minden utasítást minden alkalommal, amikor szükség van rá (pl. egy ciklusmagban) újra és újra értelmezni kell. A másik fő megközelítési irány a fordítóprogramok (compiler), melyek a forráskódot egyszer lefordítják gépi kódra, majd ezt a kódot az operációs rendszerrel később bármikor újra le lehet futtatni. A fordítás egy összetett program esetében időigényes folyamat, ám csak ritkán van rá szükség, és a futtatás sebessége már megközelíti a gépi kódú programok sebességét. Természetesen különféle köztes megoldásokra is akad példa szép számmal, de ezek tárgyalása nem feladatunk.
A C nyelvet tervezője, Dennis Ritchie, a Bell Laboratóriumban fejlesztette ki az 1970-es évek végén [5], és a UNIX operációs rendszer programnyelvének szánta. Ezt a változatot jelöli az ábrán a K&R. A C nyelv ezt követően praktikussága miatt széles körben elterjedt. Sokan készítettek sokféle C fordítót saját, vagy környezetük igényeinek megfelelően. A sokszínűségben amerikai nemzeti szabvánnyal (ANSI) teremtettek rendet az 1980-as évek végén [5]. Az ANSI C szabványt aztán Európában (ISO) is elfogadták néhány évvel később. Az ábrából látszik, hogy az ANSI C bővítette a K&R C halmazt.
A C programnyelv különböző megvalósításainak viszonya
2.1. ábra
A C általános célú programnyelv, mely tömörségéről, hatékonyságáról, gazdaságosságáról és portabilitásáról (hordozhatóságáról) ismert. (Ugyanazt a forrásszöveget különféle operációs rendszerekre és hardverekre is lefordíthatjuk, majd azt használatba vehetjük.) Nem tartalmaz túl sok vezérlési szerkezetet. Bőséges viszont az operátorkészlete, és több adattípus megléte jellemzi. Jól használható tehát műszaki-tudományos, vagy akár adatfeldolgozási problémák megoldására.
A C elég alacsony szintű - hardver közeli - programnyelv is egyben, hisz tervezője a UNIX operációs rendszert e nyelv segítségével készítette el néhány száz gépi kódú utasítás felhasználásával. A C programok gyakran ugyanolyan gyorsak, mint az Assembly "nyelven" készültek, de jóval könnyebben olvashatók és tarthatók karban.
Elektronikus jegyzetünkben nem kívánunk elmerülni konkrét integrált programfejlesztő rendszerek, operációs rendszerek és processzorok részleteinek taglalásában. Ehelyett rögzítsük azt, hogy fogalmainkkal az IBM PC kompatibilis személyi számítógépek területén maradunk! Erre a gépcsaládra is rengeteg cég készített C fordítót. Ma már azonban gyakorlatilag nem találni olyan fordítóprogramot, amelyet kizárólag C programok fordítására lehetne használni. Jellemzően olyan C++ fordítókat fejlesztenek, melyek egyúttal C fordításra is alkalmasak.
Annak érdekében, hogy a hallgatók a programozást önállóan is gyakorolhassák, és a jegyzetben szereplő példákat kipróbálhassák, módosíthassák, egy programfejlesztő környezet használatát nagyon röviden ismertetjük. A választott szoftver a Microsoft Visual Studio Community Edition 2017 (a továbbiakban: VS), mely ingyenesen letölthető és Windows operációs rendszereken használható tanulás céljára. A terméket a https://www.visualstudio.com/downloads/ weboldalról indulva lehet beszerezni. A telepítő "Workloads" ablakában lehet kiválasztani a szoftver telepítendő főbb komponenseit. Itt válasszuk a "Desktop development with C++" lehetőséget, majd a jobb oldali, "Summary" oszlopban a kategória elemei közül elegendő csak a "VC++ 2017 v141 toolset (x86, x64)" és a "Windows 10 SDK (10.0.15063.0) for Desktop C++ x86 and x64" bejegyzések előtt meghagyni a pipákat. (A bejegyzések pontos nevei az új fordító vagy operációs rendszer változatok bevezetésével változhatnak.) Ezután a telepítés az "Install" gombra kattintással indítható.
A Visual Studio telepítője
2.2. ábra
Az operációs rendszer számunkra jobbára csak olyan szempontból érdekes, hogy legyen karakteres szabványos bemenete (STanDard INput), és létezzen karakteres szabvány kimenete (STanDard OUTput), valamint szabványos hibakimenete (STtanDard ERRor output). A szabvány bemenet alapértelmezés szerint a billentyűzet, a kimenetek viszont a karakteres üzemmódú képernyőre, vagy a karakteres konzol ablakba dolgoznak. (A szabványos ki- és bemenetek célja az, hogy az adatok forrását és célját a felhasználó megváltoztathassa, így ugyanaz a program képessé válik billentyűzet helyett pl. egy fájlból vagy a hálózatról beolvasni a működéséhez szükséges adatokat.) A karakteres képernyő, vagy konzol ablak felbontása természetesen változtatható, de mi minden példánál feltételezzük a 25 sorszor 80 oszlopot! A szabvány kimeneteken a mindenkori aktuális pozíciót kurzor jelzi. A kurzor pozícióját sajnos nem tudjuk szabadon változtatni: minden kiírt karakter után eggyel jobbra mozdul, illetve a sor végéről az új sor kezdetére ugrik. Ezen kívül néhány speciális kóddal lehet pl. új sort kezdeni, vagy rögzített tabulátorhelyre ugrani, de a lehetőségek ezzel ki is merültek.
A forrásszöveg
Első közelítésben induljunk ki abból, hogy a C program (a forrásprogram) fájlazonosítójában c vagy C kiterjesztéssel rendelkező, ASCII kódú szövegfájl, mely előállítható, ill. módosítható
akármilyen ASCII kódú szövegszerkesztővel, vagy a programfejlesztő rendszer beépített szövegszerkesztőjével.
Az ASCII kódú szövegfájl sorokból áll. Egy sorban a szöveg sorbeli karaktereinek ASCII kódjai következnek rendre az egymás utáni bájtokban. A sorhoz Windows operációs rendszereken végül még két, a soremelést leíró bájt tartozik, melyekben egy soremelés (Line Feed - 10) és egy kocsi vissza (Carriage Return - 13) vezérlő karakter van. Minden más operációs rendszeren (pl. GNU/Linux, MacOS, UNIX) azonban a sorok végét csak egyetlen soremelés karakter zárja. Vigyázni kell ASCII kódú szövegfájlban a decimálisan 31 értékű bájt használatával, mert ez fájlvég jelzés a legtöbb operációs rendszerben!
Készítsük el első C programunkat, és mentsük el pelda1.c néven!
    /* PELDA1.C */
    #include <stdio.h>
    void main(void){
      printf("Ez egy C program!\n"); }
Fordítsuk le a programot és futtassuk le! A mindenki által gyanított végeredmény az a képernyőn, hogy megjelenik a szöveg, és a következő sor elején villog a kurzor:
    Ez egy C program!
A kívánt eredmény eléréséhez VS-ban a következő lépéseket kell elvégezni. A fejlesztőkörnyezetet elindítva válasszuk a "File / New / Project..." menüt. Erre azért van szükség, mert a VS-nak mindig teljes projektre van szüksége, még akkor is, ha abban csak egyetlen forrásfájl szerepel. A felbukkanó "New Project" dialógusablakban válasszuk ki a "Win32 Console Application" projekt típust, a "Name" mezőben adjunk nevet a projektnek, pl. "PrgAlapjai", majd kattintsunk az "OK" gombra!
Új projekt létrehozása
2.3. ábra
Ezután egy varázsló vezet végig a projekt létrehozásának lépésein. A "Next" gombbal a második oldalra lépve szüntessük meg a "Precompiled header" kijelölését, majd válasszuk ki az "Empty project" lehetőséget! A "Finish" gombra kattintva létrejön egy üres projekt, amiben már el lehet helyezni a lefordítani kívánt forrásfájlt.
Projekt varázsló
2.4. ábra
A "Solution explorer" ablakban a "Source files" elemen történő jobb kattintással hívjuk elő a helyi menüt, majd válasszuk az "Add / New item..." lehetőséget! Ennek hatására felbukkan az "Add New Item" dialógusablak, melyben meg kell adni az új fájl adatait. Mivel a C fájl típust nem ajánlja fel, ezért válasszuk a "C++ File"-t, majd a "Name" mezőbe írjuk be, hogy "pelda1.c"! A kiterjesztést felismerve a fejlőkörnyezet C fordítási üzemmódra fog átkapcsolni. Az ablak bezárásához kattintsunk az "Add" gombra!
Forrásfájl projekthez adása
2.5. ábra
Ezután megnyílik a szerkesztőablakban az üres forrásfájl, amibe már be lehet gépelni a fenti forrásszöveget. Érdemes ezt lementeni a "File / Save pelda1.c" menüponttal. A futtatáshoz szükséges összes résztevékenységet automatikusan elvégzi a keretrendszer, ha a "Debug / Start without debugging" menüpontra kattintunk. Ha a programot korábban még nem fordítottuk le, megjelenik egy felbukkanó ablak a "Would you like to build it?" kérdéssel, melyre feleljünk "Yes"-szel!
A fordító sikeres esetben a forrásprogramból egy vele azonos nevű (jellemzően o vagy obj kiterjesztésű) tárgymodult (object file) állít elő, és üzeneteket jelentet meg többek közt a hibákról. A VS-ban erre az "Output" ablak szolgál.
A fordítóprogram előállítja a forrásszövegből a tárgymodult
2.6. ábra
Az üzenetek legalább kétszintűek:
(fatális) hibaüzenetek (error) és figyelmeztető (warning) üzenetek.
A (fatális) hibákat, melyek jobbára szintaktikai jellegűek, mindig kijavítja a programozó, mert korrekciójuk nélkül nem készíti el a tárgymodult a fordító. A figyelmeztető üzenetekkel azonban, melyek sok esetben a legsúlyosabb problémákat jelzik, nem szokott törődni, mert a fordító létrehozza a tárgymodult, ha csak figyelmeztető üzenetekkel zárul a fordítás.
A pelda1.c programunk első sora megjegyzés (comment). A megjegyzés írásszabálya látszik a sorból, azaz:
/* karakter párral kezdődik és */ karakter párral végződik.
A megjegyzés több forrássoron át is tarthat, minden sorba is írható egy, sőt akár egy soron belül több is megadható a szintaktikai egységek között. Egyetlen tilos dolog van: a megjegyzések nem ágyazhatók egymásba!
    /* Ez a befoglaló megjegyzés eleje.
      /* Itt a beágyazott megjegyzés. */
      Ez meg a befoglaló megjegyzés vége. */
Vegyük észre, hogy bármely hibás program rögtön "hibátlanná" válik, ha /*-ot teszünk az elejére, és a záró */-t elfelejtjük megadni a további forrásszövegben!
A fordítót egybeépítették egy speciális előfeldolgozóval (preprocessor), mely az "igazi" fordítás előtt
elhagyja a forrásszövegből a megjegyzéseket, végrehajtja a neki szóló direktívákat, és ezeket is elhagyja a forrásszövegből.
Fordítás pontosabban
2.7. ábra
Az előfeldolgozó direktívák egy sorban helyezkednek el, és #-tel kezdődnek. Pontosabban # kell, legyen a sor első nem fehér karaktere (whitespace).
Fehér karakterek a szóköz, a soremelés, a lapdobás karakter, a vízszintes és a függőleges tabulátor karakter. Meg kell említeni, hogy a megjegyzés is fehér karakternek minősül. A fehér karakterek szolgálhatnak szintaktikai egységek elválasztására, de a felesleges fehér karaktereket elveti a fordító.
A pelda1.c programunk második sora egy #include direktíva, melynek hatására az előfeldolgozó megkeresi és betölti a paraméter szövegfájlt a forrásszövegbe, és elhagyja belőle ezt a direktívát.
A pillanatnyilag betöltendő szövegfájl, az stdio.h, h kiterjesztésű. Az ilyen kiterjesztésű szövegfájlokat C környezetben fejfájloknak (header) nevezik. A fejfájl egy témával kapcsolatos adattípusok, konstansok definícióit és a vonatkozó függvények jellemzőit tartalmazza.
Fedezzük fel, hogy az stdio a standard input output rövidítéséből származik, s így lényegében a szabvány kimenetet és bemenetet "kapcsoltuk" programunkhoz.
Nem volt még szó arról, hogy a paraméter fájlazonosító <> jelek között áll! A <> jel pár tájékoztatja az előfeldolgozót, hogy milyen könyvtárakban keresse a szövegfájlt. A programfejlesztő rendszer beállításai közt van egy, melynek segítségével megadhatók a fejfájlok (include fájlok) keresési útjai. <fájlazonosító> alakú paraméter hatására az #include direktíva csak a programfejlesztő rendszerben előírt utakon keresi a fájlt, és sehol másutt.
pelda1.c programunk harmadik és negyedik sora a main (fő) függvény definíciója. A függvénydefiníció szintaktikai alakja:
    típus függvénynév(formális-paraméterlista) { függvény-test }
A visszatérési érték típusa void, mely kulcsszó éppen azt jelzi, hogy a függvénynek nincs visszaadott értéke. A függvénynév main. C környezetben a main az indító program. A formális paraméterlista mindig ()-ben van. Pillanatnyilag itt is a void kulcsszó látható, ami e helyt azt rögzíti, hogy nincs formális paraméter. A függvény-test-et mindig {}-ben, úgy nevezett blokk zárójelekben, kell elhelyezni.
A { nyitja, a } zárja a blokkot. A { } párok összetartoznak. A } mindig a forrásszövegben őt megelőző {-t zárja.
A forrásprogram több forrásmodulban (forrásfájlban) is elhelyezhető. Végrehajtható program létrehozásához azonban valamelyik forrásmodulnak tartalmaznia kell a main-t. Az indító program a végrehajtható program belépési pontja is egyben. Ez az a hely, ahova a memóriába történt betöltés után átadja a vezérlést az operációs rendszer.
Az indító programnak természetesen a végrehajtható program "igazi" indítása előtt el kell látnia néhány más feladatot is, s a programozó által írt main-beli függvénytest csak ezután következik. A gyártók az indító programot rendszerint tárgymodul alakjában szokták rendelkezésre bocsátani.
A pelda1.c programban a main függvény teste egyetlen függvényhívás. A függvényhívás szintaktikája:
    függvénynév(aktuális-paraméterlista)
A függvénynév a printf, mely függvény az aktuális paraméterét megjelenteti a szabvány kimeneten. Ennek használata érdekében kellett bekapcsolnunk az stdio.h fejfájlt. A ()-ben álló aktuális paraméterlista egytagú, és momentán egy karakterlánc konstans. A karakterlánc konstans írásszabálya látszik: idézőjelek közé zárt karaktersorozat.
A példabeli karakterlánc konstans végén azonban van egy kis furcsaság: a \n! Emlékezzünk vissza rá, hogy az első 32 kód vezérlőjeleket kódol, és valahogyan azt is biztosítania kell a programnyelvnek, hogy ezek a karakterek, ill. a karakterkép nélküli kódpozíciók is megadhatók legyenek. A C programnyelvben erre a célra az úgynevezett escape szekvencia (escape jelsorozat, escape sequence) szolgál.
Remélhetőleg világossá vált az előző okfejtésből, hogy az escape szekvencia helyfoglalása a memóriában egyetlen bájt!
Az escape szekvencia \ jellel kezdődik, s ezután egy karakter következik. A teljesség igénye nélkül felsorolunk itt néhányat!
Escape szekvencia | Jelentés
\b | visszatörlés (back space)
\n | soremelés vagy új sor (line feed)
\r | kocsi vissza (carriage return)
\t | vízszintes tabulátor (horizontal tab)
\" | egyetlen " karakter
\\ | egyetlen \ karakter
\0 | karakterlánc záró bájt, melynek minden bitje zérus
\ooo | az o-k oktális számjegyek
Vegyük észre, hogy ha idézőjelet kívánunk a karakterlánc konstansba írni, akkor azt csak \" módon tehetjük meg! Ugyanez a helyzet az escape szekvencia kezdőkarakterével, mert az meg csak megkettőzve képez egy karaktert! Lássuk még be, hogy a \ooo alakkal az ASCII kódtábla bármely karaktere leírható! Például a \012 azonos a \n-nel, vagy a \060 a 0 számjegy karakter.
A printf függvényhívás után álló ; utasításvég jelzés.
pelda1.c programunk "utolsó fehér foltja" a printf, mely egyike a szabványos bemenet és kimenet függvényeinek.
Kapcsoló-szerkesztés (link)
A gyártók a szabvány bemenet, kimenet és más témacsoportok függvényeinek tárgykódját statikus könyvtárakban (a vagy lib kiterjesztésű fájlokban) helyezik el. Nevezik ezeket a könyvtárakat futásidejű könyvtáraknak (runtime libraries), vagy szabvány könyvtáraknak (standard libraries) is. A könyvtárfájlokból a szükséges tárgykódot a kapcsoló-szerkesztő (linker) másolja hozzá a végrehajtható fájlhoz. Lássuk ábrán is a szerkesztést!
Kapcsoló-szerkesztés
2.8. ábra
A programfejlesztő rendszerben be lehet állítani a statikus könyvtárfájlok (library) keresési útjait. Azt is meg lehet adni természetesen, hogy hova kerüljenek a fordítás és a kapcsoló-szerkesztés során keletkező kimeneti fájlok, de az alapértelmezett értékek többnyire megfelelőek.
Futtatás
A végrehajtható fájl a parancssorból azonosítójának begépelésével indítható. Amennyiben egy integrált szoftverfejlesztő környezetet (Integrated Development Environment, IDE) használunk (mint pl. a VS), valószínűleg annak menüjében is van valahol egy pont, mellyel az aktuális végrehajtható fájl futtatható. Többnyire létezik a három lépést (fordítás, kapcsoló-szerkesztés és futtatás) egymás után megvalósító egyetlen menüpont is (VS-ban: "Debug / Start without debugging" menüpont).
Táblázat készítése
Készítsük el a következő forint-euró átszámítási táblázatot! Egy euró pillanatnyilag legyen 310 forint!
Forint | Euró
100 | 0.32
200 | 0.65
... | ...
1000 | 3.23
Adatstruktúra:
A változók a valós típusú euro-n kívül, mind egészek. also lesz a tartomány alsó határa, felso a tartomány felső értéke, lepes a lépésköz, és ft a ciklusváltozó.
Algoritmus:
Deklaráljuk a változókat! Ellátjuk őket - az euro-tól eltekintve - kezdőértékkel. Megjelentetjük a táblázat fejléc sorát és az aláhúzást. Működtetjük a ciklust, míg ft <= felso. A ciklusmagban kiszámítjuk az aktuális euro értéket, megjelentetjük az összetartozó ft - euro értékpárt, s végül léptetjük az ft ciklusváltozót.
Készítsük el a programot!
    /* PELDA2.C: Forint-euro atszamitasi tablazat */
    #include <stdio.h>
    void main(void){
      int also, felso, lepes, ft; /* Deklaraciok */
      float euro;
      also = 100; /* Vegrehajthato utasitasok */
      felso = 1000;
      lepes = 100;
      ft = also;
      printf("   Forint|     Euro\n"
             "---------+---------\n");
      while(ft <= felso){ /* Beagyazott (belso) blokk */
        euro = ft / 310.;
        printf("%9d|%9.2f\n", ft, euro);
        ft = ft + lepes; } }
A pelda2.c-ből kitűnően látszik a C függvény szerkezete, azaz az úgy nevezett blokkszerkezet:
Előbb a deklarációs utasítások jönnek a blokkbeli változókra. A C szigorú szintaktikájú nyelv: előzetes deklaráció nélkül nem használhatók benne a változók, és kivétel nélkül deklarálni kell minden használatos változót! Aztán a végrehajtható utasítások következnek.
A blokkszerkezet deklarációs és végrehajtható részre bontása a C-ben szigorú szintaktikai szabály, azaz egyetlen végrehajtható utasítás sem keveredhet a deklarációs utasítások közé, ill. a végrehajtható utasítások között nem helyezkedhet el deklarációs utasítás. (A C99 vagy újabb szabványt teljesítő fordítók megengedik a deklarációs és a végrehajtható utasítások keverését feltéve, hogy a deklaráció megelőzi a deklarált változó első felhasználását.) Természetesen a végrehajtható utasítások közé beágyazott (belső) blokkban a szabály újra kezdődik.
Vegyük észre a példaprogramunk végén elhelyezkedő beágyazott vagy belső blokkot! Figyeljük meg a main két első sorában, hogy a deklarációs utasítás szintaktikája:
    típus azonosítólista;
Az azonosítólista azonosítók sorozata egymástól vesszővel elválasztva. Megfigyelhető még, hogy az azonosítók képzéséhez ékezetes betűk nem használhatók!
Foglalkozzunk kicsit a típusokkal!
Az int (integer) 16, vagy 32 bites, alapértelmezés szerint előjeles (signed), fixpontos belsőábrázolású (kettes komplemens alakban felírt) egész típus. Előjeltelen (unsigned) esetben nincsenek negatív értékek.
A float (floating point) 4 bájtos, lebegőpontos belsőábrázolású valós típus, ahol a mantissza és előjele 3 bájtot, s a karakterisztika előjelével egy bájtot foglal el. Az ábrázolási határok: ±3.4*10-38 - ±3.4*10+38. Ez a mantissza méret 6 - 7 decimális jegy pontosságot tesz lehetővé.
Néhány programfejlesztő rendszer a lebegőpontos könyvtárakat (lib) csak akkor kapcsolja be a kapcsoló-szerkesztő által keresésnek alávethető könyvtárak közé, ha a programban egyáltalán igény jelentkezik valamilyen lebegőpontos ábrázolás, vagy művelet elvégeztetésére, vagy ha azt kifejezetten kéri a programozó.
A pelda2.c végrehajtható részének első négy utasítása értékadás. Ki kell azonban hangsúlyozni, hogy a C-ben nincs értékadó utasítás, csak hozzárendelés operátor, s a hozzárendelésekből azért lesz utasítás, mert ;-t írtunk utánuk.
A hozzárendelésre rögtön visszatérünk! Vegyük észre előbb az egész konstans írásszabályát! Elhagyható előjellel kezdődik, s ilyenkor pozitív, és ezután az egész szám jegyei következnek.
A fejléc sort és az aláhúzást egyetlen printf függvényhívással valósítottuk meg. Látszik, hogy a táblázat oszlopait 9 karakter szélességűre választottuk.
Figyeljük meg, hogy a pontos pozícionálást segítendő a fejléc sort és az aláhúzást a printf-ben két egymás alá írt karakterlánc konstansként adtuk meg! A C fordító a csak fehér karakterekkel elválasztott karakterlánc konstansokat egyesíti egyetlen karakterlánc konstanssá, s így a példabeli printf-nek végül is egyetlen paramétere van.
A pelda2.c-ben az elöltesztelő ciklusutasítás következik, melynek szintaktikája:
    while(kifejezés) utasítás
A kifejezés aritmetikai, azaz számértékű. Az elöltesztelő ciklusutasítás hatására lépésenként a következő történik:
1. | Kiértékeli a fordító a kifejezést. Ha hamis (zérus), akkor vége a ciklusnak, s a while-t követő utasítás jön a programban.
2. | Ha a kifejezés igaz (nem zérus), akkor az utasítás végrehajtása, és aztán újból az 1. pont következik.
Az utasítás állhat több utasításból is, csak {}-be kell tenni őket. A {}-ben álló több utasítást összetett utasításnak (compound statement) nevezik. Az összetett utasítás szintaktikailag egyetlen utasításnak minősül.
A példabeli belső blokk első és utolsó utasítása hozzárendelés, melynek szintaktikai alakja:
    objektum = kifejezés
A hozzárendelés operátor (műveleti jel) bal oldalán valami olyan objektumnak kell állnia, ami értéket képes felvenni. A szaknyelv ezt módosítható balértéknek nevezi. Példánkban az összes hozzárendelés bal oldalán egy változó azonosítója áll. Az = jobb oldalán meghatározható értékű kifejezésnek (jobbértéknek) kell helyet foglalnia. A hozzárendelés hatására a kifejezés értéke - esetlegesen az objektum típusára történt konverzió után - felülírja az objektum értékét. Az egész "konstrukció" értéke az objektum új értéke, és típusa az objektum típusa.
A legutóbbi mondat azt célozza, hogy ha a hozzárendelés kifejezés része, akkor ez az érték és típus vesz részt a kifejezés további kiértékelésében.
Vegyük észre, hogy az eddigi printf függvényhívásainknak egyetlen karakterlánc konstans paramétere volt, mely változatlan tartalommal jelent meg a karakteres képernyőn! A belső blokkbeli printf-ben viszont három aktuális paraméter van: egy karakterlánc konstans, egy int és egy float. A gond ugye az, hogy az int és a float paraméter értékét megjelentetés előtt karakterlánccá kéne konvertálni, hisz bináris bájtok képernyőre vitelének semmiféle értelme nincs!
A printf első karakterlánc paramétere
karakterekből és formátumspecifikációkból
áll. A karakterek változatlanul jelennek meg a képernyőn, a formátumspecifikációk viszont meghatározzák, hogy a printf további paramétereinek értékeit milyen módon kell karakterlánccá alakítani, s aztán ezt hogyan kell megjelentetni.
A formátumspecifikáció % jellel indul és típuskarakterrel zárul. A formátumspecifikációk és a printf további paraméterei balról jobbra haladva rendre összetartoznak. Sőt ugyanannyi formátumspecifikáció lehet csak, mint ahány további paraméter van.
Felsorolunk néhány típuskaraktert a következő táblázatban:
Típuskarakter | A hozzátartozó paraméter típusa | Megjelenítés
d | egész típusú | decimális egészként
f | lebegőpontos | tizedes tört alakjában
c | egy karakter | karakterként
s | karakterlánc | karakterláncként
A formátumspecifikáció pontosabb alakja:
    %<szélesség><.pontosság>típuskarakter
A <>-be tétel az elhagyhatóságot hivatott jelezni. A szélesség annak a mezőnek a karakteres szélességét rögzíti, amiben a karakterlánccá konvertált értéket - alapértelmezés szerint jobbra igazítva, és balról szóközfeltöltéssel - kell megjelentetni. Ha a szélességet elhagyjuk a formátumspecifikációból, akkor az adat a szükséges szélességben jelenik meg. Maradjunk annyiban pillanatnyilag, hogy a pontosság lebegőpontos esetben a tizedes jegyek számát határozza meg!
Lássuk be, hogy a "%9d|%9.2f\n" karakterlánckonstansból a %9d és a %9.2f formátumspecifikációk, míg a | és a \n sima karakterek! Vegyük észre, hogy a "nagy" pozícionálgatás helyett táblázatunk fejléc sorának és aláhúzásának megjelentetését így is írhattuk volna:
    printf("%9s|%9s\n---------+---------\n", "Forint", "Euró");
Foglalkoznunk kell még egy kicsit a műveletekkel! Vannak
egyoperandusos (operátor operandus) és kétoperandusos (operandus operátor operandus)
műveletek. Az egyoperandusos operátorokkal kevés probléma van. Az eredmény típusa többnyire egyezik az operandus típusával, és az értéke az operandus értékén végrehajtva az operátort. Például: -változó. Az eredmény típusa a változó típusa, és az eredmény értéke a változó értékének -1-szerese.
Problémák a kétoperandusos műveleteknél jelentkezhetnek, és hanyagoljuk el a továbbiakban az eredmény értékét! Ha kétoperandusos műveletnél a két operandus típusa azonos, akkor az eredmény típusa is a közös típus lesz. Ha a két operandus típusa eltér, akkor a fordító a rövidebb, pontatlanabb operandus értékét a hosszabb, pontosabb operandus típusára konvertálja, és csak ezután végzi el a műveletet. Az eredmény típusa természetesen a hosszabb, pontosabb típus. A ft/310. osztásban a ft egész típusú és a 310. konstans lebegőpontos. A művelet elvégzése előtt a ft értékét lebegőpontossá alakítja a fordító, és csak ezután hajtja végre az osztást. Az eredmény tehát ugyancsak lebegőpontos lesz. Ezt implicit típuskonverziónak nevezik.
Vegyük észre közben a valós konstans írásszabályát is! Elhagyható előjellel kezdődik, amikor is pozitív, és aztán az egész rész jegyei jönnek. Aztán egy tizedespont után a tört rész számjegyei következnek.
Ismét felhívjuk a figyelmet arra, hogy egészek osztásának eredménye is egész, és nincs semmiféle maradékmegőrzés, lebegőpontos átalakítás! Alakítsuk csak át az euro=ft/310. hozzárendelést euro=ft/310-re, s rögtön láthatjuk, hogy az eredményekben sehol sincs tört rész!
Felvetődik a kérdés, hogyan lehetne ilyenkor a helyes értéket meghatározni? A válasz: explicit típusmódosítás segítségével, melynek szintaktikai alakja:
    (típus)kifejezés
Hatására a kifejezés értékét típus típusúvá alakítja a fordító. A konkrét esetben az osztás legalább egyik operandusát float-tá kéne módosítani, és ugye akkor a kétoperandusos műveletekre megismert szabály szerint a másik operandus értékét is azzá alakítaná a fordító a művelet tényleges elvégzése előtt, azaz:
    euro = (float)ft / 310;
A forint-euró átszámítási táblázatot elkészítő pelda2.c megoldásunkkal az a "baj", hogy túl sok változót használunk. Könnyen beláthatjuk, hogy az ft-től eltekintve a többi változó nem is az, hisz a program futása alatt nem változtatja meg egyik sem az értékét! Készítsünk pelda3.c néven egy jobb megoldást!
    /* PELDA3.C: Forint-euro atszamitasi tablazat */
    #include <stdio.h>
    void main(void){
      int ft;
      printf("%9s|%9s\n---------+---------\n",
             "Forint", "Euro");
      for(ft=100; ft<=1000; ft=ft+100)
        printf("%9d|%9.2f\n", ft, ft/310.); }
pelda3.c programunkban két új dolog látható. Az egyik a
    for(<init-kifejezés>; <kifejezés>; <léptető-kifejezés>) utasítás
elöltesztelő, iteratív ciklusutasítás, melynek végrehajtása a következő lépések szerint történik meg:
1. | A fordító végrehajtja az init-kifejezést, ha van. Az elhagyhatóságot most is <> jelekkel szemléltetjük!
2. | Kiértékeli a kifejezést. Ha hamis (zérus), akkor vége a ciklusnak, s a for-t követő utasítás jön a programban. Látható, hogy a szintaktika szerint ez a kifejezés is elhagyható. Ilyenkor 1-nek (igaznak) minősül.
3. | Ha a kifejezés igaz (nem zérus), akkor az utasítás végrehajtása jön. Az utasítás most is lehetne összetett utasítás is!
4. | Végül az ugyancsak elhagyható léptető-kifejezés, majd újból a 2. pont következik.
Ha a for utasítást while-lal szeretnénk felírni, akkor azt így tehetjük meg:
    <init-kifejezés>;
    while(kifejezés) { utasítás; <léptető-kifejezés>; }
A szintaktikai szabályt összefoglalva: a for-ból akár mindegyik kifejezés is elhagyható, de az első kettőt záró pontosvesszők nem!
A pelda3.c programbeli másik új dolog az, hogy a printf aktuális paramétereként kifejezés is megadható.
A pelda3.c ugyan sokat rövidült, de ezzel a megoldással meg az a probléma, hogy tele van varázs-konstansokkal. Ha megváltoztatnánk átszámítási táblázatunkban a tartomány alsó, ill. felső határát, módosítanánk a lépésközt, vagy az euró árfolyamot, akkor ennek megfelelően át kellene írnunk varázs-konstansainkat is. Az ilyen átírogatás 8 soros programnál könnyen, és remélhetőleg hibamentesen megvalósítható. Belátható azonban, hogy nagyméretű, esetleg több forrásfájlból álló szoftver esetében, amikor is a varázs-konstansok rengeteg helyen előfordulhatnak, ez a módszer megbízhatatlan, vagy legalább is nagyon hibagyanús.
A C a probléma megoldására a szimbolikus konstansok, vagy más megnevezéssel: egyszerű makrók, használatát javasolja. A metódus a következő:
1. | Definiálni kell a benne használatos szimbolikus állandókat egy helyen, egyszer, a forrásfájl elején.
2. | Aztán a programban végig a konstansok helyett szisztematikusan a szimbolikus konstansokat kell használni.
A változtatás is nagyon egyszerűvé válik így:
a megváltozott konstans értékét egy helyen át kell írni, s a többi felhasználása automatikusan módosul a következő fordításnál.
A szimbolikus állandó a
    #define azonosító helyettesítő-szöveg
előfeldolgozó direktívával definiálható. A szimbolikus állandó - tulajdonképpen az azonosító - a direktíva helyétől a forrásszöveg végéig van érvényben. Az előfeldolgozó kihagyja a direktívát a forrásszövegből, majd végigmegy rajta, és az azonosító minden előfordulását helyettesítő-szövegre cseréli.
Lássuk a "medvét"!
    /* PELDA4.C: Forint-euro atszamitasi tablazat */
    #include <stdio.h>
    #define ALSO 100       /* A tartomany also hatara */
    #define FELSO 1000     /* A tartomany felso erteke */
    #define LEPES 100      /* A lepeskoz */
    #define ARFOLYAM 310.  /* Ft/euro arfolyam */
    void main(void){
      int ft;
      printf("%9s|%9s\n---------+---------\n",
             "Forint", "Euró");
      for(ft=ALSO; ft<=FELSO; ft=ft+LEPES)
        printf("%9d|%9.2f\n", ft, ft/ARFOLYAM); }
Szokás még - különösen több forrásmodulos esetben - a #define direktívákat (és még más dolgokat) külön fejfájlban elhelyezni, s aztán ezt minden forrásfájl elején #include direktívával bekapcsolni. Készítsük csak el ezt a variációt is!
    /* PELDA5.H: Fejfajl az atszamitasi tablahoz */
    #include <stdio.h>
    #define ALSO 100       /* A tartomany also hatara */
    #define FELSO 1000     /* A tartomany felso erteke */
    #define LEPES 100      /* A lepeskoz */
    #define ARFOLYAM 310.  /* Ft/euro arfolyam */

    /* PELDA5.C: Forint-euro atszamitasi tablazat */
    #include "pelda5.h"
    void main(void){
      int ft;
      printf("%9s|%9s\n---------+---------\n",
             "Forint", "Euro");
      for(ft=ALSO; ft<=FELSO; ft=ft+LEPES)
        printf("%9d|%9.2f\n", ft, ft/ARFOLYAM); }
Vegyük észre, hogy az #include direktíva fájlazonosítója nem <>-k, hanem ""-k között áll! Ennek hatására az előfeldolgozó a megadott azonosítójú fájlt először az aktuális mappában - abban a könyvtárban, ahol az a .c fájl is elhelyezkedik, melyben a #include direktíva volt - keresi. Ha itt nem találja, akkor továbbkeresi a programfejlesztő rendszerben beállított utakon.
Bemenet, kimenet
A kissé "lerágott csont" forint-euró átszámítási táblázatos példánkban nem volt bemenet. Megtanultuk már, hogy a szabvány bemenet és kimenet használatához be kell kapcsolni az stdio.h fejfájlt:
    #include <stdio.h>
Alapértelmezés szerint szabvány bemenet (stdin) a billentyűzet, és szabvány kimenet (stdout) a képernyő. A legtöbb operációs rendszerben azonban mindkettő átirányítható szövegfájlba is.
Egy karaktert olvas be a szabvány bemenetről az
    int getchar(void);
függvény. Ezt aztán balról zérus feltöltéssel int-té típusmódosítja, és visszaadja a hívónak.
Azt, ahogyan az előbb a getchar-t leírtuk, függvény prototípusnak nevezik. A függvény prototípus teljes formai információt szolgáltat a szubrutinról, azaz rögzíti:
a függvény visszatérési értékének típusát, a függvény nevét, paramétereinek számát, sorrendjét és típusát.
A getchar fájl végén, vagy hiba esetén EOF-ot (End Of File) szolgáltat. Az EOF az stdio.h fejfájlban definiált szimbolikus állandó:
    #define EOF (-1)
Tekintsük csak meg az stdio.h fejfájlban! Nézegetés közben vegyük azt is észre, hogy a fejfájl tele van függvény prototípusokkal.
Már csak az a kérdés maradt, hogy mi a fájlvég a szabvány bemeneten, ha az a billentyűzet? Egy operációs rendszertől függő billentyűkombináció: Ctrl+Z vagy Ctrl+D.
A paraméter karaktert kiviszi a szabvány kimenet aktuális pozíciójára az
    int putchar(int k);
és sikeres esetben vissza is adja ezt az értéket. A hibát épp az jelzi, ha a putchar szolgáltatta érték eltér k-tól.
Készítsünk programot, ami a szabvány bemenetet átmásolja a szabvány kimenetre!
    /* PELDA6.C: Bemenet masolasa a kimenetre */
    #include <stdio.h>
    void main(void){
      int k;
      printf("Bemenet masolasa a kimenetre:\n"
             "Gepeljen Ctrl+Z-ig sorokat!\n\n");
      k=getchar();
      while(k!=EOF){
        if(k!=putchar(k))
          printf("Hiba a kimeneten!\n");
        k=getchar(); } }
Fogalmazzuk meg minimális elvárásainkat egy programmal szemben!
A szoftver indulásakor jelezze ki, hogy mit csinál! Ha valamilyen eredményt közöl, akkor azt lássa el tájékoztató szöveggel, mértékegységgel stb.! Ha valamit bekér, akkor tájékoztasson róla, hogy mit kell megadni, milyen egységben stb.! A bemenetet célszerű ellenőrizni, a hibás adat helyett - a hiba okát esetleg kijelezve - újat kérni. Hibás kiinduló adatokból csak hibás eredményre lehet jutni.
A kétirányú szelekció szintaktikai alakja:
    if(kifejezés) utasítás1
    <else utasítás2>
Az elhagyhatóságot most is a <> jelzi. Ha a kifejezés igaz (nem zérus), akkor utasítás1 végrehajtása következik. Ha a kifejezés hamis (zérus) és van else rész, akkor az utasítás2 következik. Mindkét utasítás összetett utasítás is lehet.
    while((k=getchar())!=EOF)
      if(k!=putchar(k))
        printf("Hiba a kimeneten!\n");
A while kifejezése egy nem egyenlő reláció, melynek bal oldali operandusa egy külön zárójelben álló hozzárendelés. Előbb a hozzárendelés jobb oldalát kell kiértékelni. Lássuk csak sorban a kiértékelés lépéseit!
1. | Meghívja a getchar függvényt a fordító.
2. | A visszakapott értékkel felülírja k változó értékét.
3. | A getchar-tól kapott értéket hasonlítja EOF-hoz.
A kifejezésből a hozzárendelés körüli külön zárójel nem hagyható el, mert a hozzárendelés alacsonyabb prioritású művelet a relációnál. Ha mégis elhagynánk, akkor a kiértékelés során a fordító:
1. | Meghívná előbb a getchar függvényt.
2. | A visszatérési értéket hasonlítaná EOF-hoz. Tehát kapna egy logikai igaz (1), vagy hamis (0) értéket!
3. | A k változó felvenné ezt az 1, vagy 0 értéket.
Figyeljük meg a pelda6.c futtatásakor, hogy a getchar a bemenetről olvasott karaktereket az operációs rendszer billentyűzet pufferéből kapja! Emlékezzünk csak vissza! A parancssorban a begépelt szöveget szerkeszthetjük mindaddig, míg Enter-t nem nyomunk. A billentyűzet pufferben levő karakterek tehát csak akkor állnak a getchar rendelkezésére, ha a felhasználó leütötte az Enter billentyűt.
Készítsünk programot, mely fájlvégig leszámlálja, hogy hány
numerikus karakter, fehér karakter, más egyéb karakter és összesen hány karakter
érkezett a szabvány bemenetről!
Megoldásunkban az összes változó egész típusú. k tartalmazza a beolvasott karaktert. A num, a feher és az egyeb számlálók. Az algoritmus:
Deklaráljuk a változókat, és az összes számlálót lássuk el zérus kezdőértékkel! Jelentessük meg a program címét, és tájékoztassunk a használatáról! Működtessük addig a ciklust, míg EOF nem érkezik a bemenetről! A ciklusmagban háromirányú szelekció segítségével el kell ágazni a három kategória felé, és ott meg kell növelni eggyel a megfelelő számlálót! A ciklus befejeződése után megjelentetendők a számlálók értékei megfelelő tájékoztató szövegekkel, és az is, hogy összesen hány karakter érkezett a bemenetről!
    /* PELDA7.C: A bemenet karaktereinek
       leszamlalasa kategoriankent */
    #include <stdio.h>
    void main(void){
      short k, num, feher, egyeb;
      num=feher=egyeb=0;
      printf("Bemenet karakterek leszamlalasa\n"
             "kategoriankent EOF-ig, vagy Ctrl+Z-ig.\n");
      while((k=getchar())!=EOF)
        if(k>='0' && k<='9')++num;
        else if(k==' '||k=='\n'||k=='\t')++feher;
        else ++egyeb;
      printf("Karakter szamok:\n"
             "----------------\n"
             "numerikus: %5hd\n"
             "feher:     %5hd\n"
             "egyeb:     %5hd\n"
             "----------------\n"
             "ossz: %10ld\n",
             num, feher, egyeb, (long)num+feher+egyeb); }
Pontosítani kell a deklarációs utasítás eddig megismert szintaktikáját!
    <típusmódosítók> <alaptípus> azonosítólista;
Az elhagyható alaptípus alapértelmezés szerint int. Az ugyancsak elhagyható típusmódosítók az alaptípus valamilyen jellemzőjét változtatják meg. int típus esetén:
Az egész alapértelmezés szerint előjeles (signed), és lehetne még előjeltelen (unsigned). A signed és az unsigned módosítók egymást kizáróak. Két, egymást kizáró hosszmódosítóval az egész belsőábrázolása bizonyosan 16 bites (short), ill. biztos 32 bites (long).
Végül is a különféle int típusok méretei így összegezhetők:
    short <= int <= long
Vegyük észre, hogy az ismertetett szabályok szerint a short, a short int és a signed short int azonos típusok. A short és a short int írásmódnál figyelembe vettük, hogy signed az alapértelmezés. A short felírásakor még arra is tekintettel voltunk, hogy a meg nem adott alaptípus alapértelmezése int. Ugyanezek mondhatók el a long, a long int és a signed long int vonatkozásában is.
Ugyan a szintaktika azt mutatja, de a deklarációs utasításban a típusmódosítók és az alaptípus egyszerre nem hagyhatók el!
Feltéve, hogy a, b és c balértékek, az
    a=b=c=kifejezés
értelmezése megint abból fakad, hogy a hozzárendelés a C-ben operátor,
azaz:
    a=(b=(c=kifejezés))
A fordító jobbról balra halad, azaz kiértékeli a kifejezést, és visszafelé jövet beírja az eredményt a balértékekbe. A konstrukció hatására a fordító gyorsabb kódot is hoz létre. Ugyanis a
    c=kifejezés;
    b=kifejezés;
    a=kifejezés;
írásmódnál háromszor kell kiértékelni ugyanazt a kifejezést.
C-ben a többágú (N) szelekcióra az egyik kódolási lehetőség:
    if(kifejezés1)utasítás1
    else if(kifejezés2)utasítás2
    else if(kifejezés3)utasítás3
    /* . . . */
    else utasításN
Ha valamelyik if kifejezése igaz (nem zérus) a konstrukcióban, akkor a vele azonos sorszámú utasítás végrehajtása következik, majd a konstrukciót követő utasítás jön. Ha minden kifejezés hamis (zérus), akkor viszont utasításN hajtandó végre.
Fedezzük fel a karakter konstans írásszabályát: aposztrófok között karakter, vagy escape szekvencia. A karakter konstans belsőábrázolása int, így az ASCII kód egész értéknek is minősül kifejezésekben.
A kétoperandusos logikai operátorok prioritása alacsonyabb a relációkénál, és az && magasabb prioritású, mint a ||. Ha a kétoperandusos logikai művelet eredménye eldől a bal oldali operandus kiértékelésével, akkor a C bele sem kezd a másik operandus értékelésébe. Az és művelet eredménye eldőlt, ha a bal oldali operandus hamis. A vagy pedig akkor kész, ha az első operandus igaz.
A C-ben van inkrementálás (növelés, ++) és dekrementálás (csökkentés, --) egész értékekre. Mindkét művelet egyoperandusos, tehát nagyon magas prioritású. A ++ operandusa értékét eggyel növeli meg, s a -- pedig eggyel csökkenti, azaz:
    ++változó  változó=változó+1
    --változó  változó=változó-1
A problémák ott kezdődnek azonban, hogy mindkét művelet létezik előtag (prefix) és utótag (postfix) operátorként is! Foglalkozzunk csak a ++ operátorral! A ++változó és a változó++ hatására a változó értéke eggyel mindenképp megnövekedik. Kifejezés részeként előtag operátor esetén azonban a változó új értéke vesz részt a további kiértékelésben, míg utótag műveletnél a változó eredeti értéke számít be. Feltéve, hogy a és b egész típusú változók, és b értéke 6:
    a = ++b; /* a=7 és b=7 */
    a = b++; /* a=7 és b=8 */
Figyeljünk fel rá, hogy a pelda7.c utolsó printf utasításában hosszmódosítók állnak a d típuskarakterek előtt a formátumspecifikációkban! Látszik, hogy a h jelzi a printf-nek, hogy a formátumspecifikációhoz tartozó aktuális paraméter short típusú (2 bájtos), ill. l tudatja vele, hogy a hozzátartozó aktuális paraméter long (4 bájtos).
A megfelelő hosszmódosítók megadása a formátumspecifikációkban elengedhetetlen, hisz nem mindegy, hogy a függvény a verem következő hány bájtját tekinti a formátumspecifikációhoz tartozónak!
A verem (stack) alatt itt azt a memóriaterületet értjük, amelyet a fordító a függvény paramétereinek átadásához illetve a hívás helyének tárolásához használ, ahol a hívó függvény futtatása a hívott függvény futásának befejeztével folytatódhat, de a fogalom jelentése ennél általánosabb. Verem alatt többnyire egy LIFO (Last In First Out) adatszerkezetet értenek, azaz olyan tárolót, amelyből mindig az utoljára belé helyezett adatot lehet kinyerni. Ennek érdekében legalább két műveletet is szokás biztosítani: a push (kb. rárak) segítségével tárolható el egy adat, és a pop (kb. levesz) művelettel olvasható ki, ami azt el is távolítja egyben a tárból. Ezt általában egy véges méretű összefüggő memóriaterülettel (mint pl. egy tömb) és egy veremmutató segítségével lehet kivitelezni, ahol a veremmutató megvalósítástól függően az utolsó verembe helyezett, vagy a következő alkalommal a verembe helyezendő adat helyét jelöli ki a vermen belül. Értékét mind a push, mind a pop művelet végrehajtását követően módosítani kell. Teli verembe értelemszerűen nem lehet további adatokat elhelyezni, üresből pedig eltávolítani, ilyenkor túl- vagy alulcsordul a verem. A vermet sokszor hardver szinten valósítják meg.
Vegyük azt is észre, hogy a típusokhoz a mezőszélességgel is felkészültünk: a maximális pozitív short érték bizonyosan elfér 5 pozíción, s long pedig 10-en! Látható még, hogy arra is vigyáztunk, hogy a három maximális short érték összege részeredményként se csonkuljon! Ezért az explicit long-gá módosítás a printf utolsó paraméterében:
    (long)num+feher+egyeb

| Feladatok
| 1. Mit fog megjeleníteni a program a szabványos kimeneten, ha a /* ? */ helyére a következőket írjuk? #include<stdio.h> void main(void) { int v; v = 1; /* ? */ printf("%d", v); }
Beszúrt sor | Kimenet
v = v + 1; |
v = v * 2; |
v = -v; |
v = v * v; |
| 2. Mit fog megjeleníteni a program a szabványos kimeneten, ha a /* ? */ helyére a következőket írjuk? #include<stdio.h> void main(void) { int v1, v2, w; v1 = 2; v2 = 3; /* ? */ printf("%d", w); }
Beszúrt sor | Kimenet
w = v1 + v2; |
w = v1 * v2; |
w = v1 / v2; |
w = v1 % v2; |
w = v1 - v2; |
| 3. Mit fog megjeleníteni a program a szabványos kimeneten, ha a /* ? */ helyére a következőket írjuk? #include<stdio.h> void main(void) { int v; v = 12; if(/* ? */) printf("igen"); else printf("nem"); }
Beszúrt szöveg | Kimenet
v % 2 |
v > 0 |
v>=-5 && v<=5 |
v%10==0 && v>42 |
v%3==0 || v<=50 |
| 4. Mit fog megjeleníteni a program a szabványos kimeneten, ha a /* ? */ helyére a következőket írjuk? #include<stdio.h> void main(void) { int i, n; n = 5; for(i=0; i<n; i++) printf("%d ", /* ? */); }
Beszúrt szöveg | Kimenet
i |
2 * i |
i * n |
i % 2 |
-i |
| 5. Olyan programot szeretnénk készíteni, amelyik a karakteres üzemmódú képernyő egy 21x21-es ablakában csillag karakterekkel megjelentet egy keresztet a 10. sor és 10. oszlop feltöltésével. Jelölje meg azokat a sorokat, amelyeket a /* ? */ helyére helyettesítve helyes működést kapunk! #include <stdio.h> void main(void) { int i; /* ? */ if (i==10) printf("*********************\n"); else printf(" *\n"); }
| for (i=1;i<=21;i++)
| for (i=1;i<22;i++)
| for (i=0;i<=21;i++)
| for (i=0;i<21;i++)
| 6. Olyan programot szeretnénk készíteni, amelyik a karakteres üzemmódú képernyő egy 21x21-es ablakában csillag karakterekkel megjelenteti a főátlót (bal felső sarokból a jobb alsóba menőt). Jelölje meg azokat a sorokat, amelyeket a /* ? */ helyére helyettesítve helyes működést kapunk! #include <stdio.h> void main(void) { int i,j; for (i=0;i<21;i++) { for (/* ? */) printf(" "); printf("*\n"); } }
| j=0;j!=i;j++
| j=0;j<=i;j++
| j=21;j>i;j--
| j=0;j<i;j++
| 7. Olyan programot szeretnénk készíteni, amelyik a karakteres üzemmódú képernyő egy 21x21-es ablakában csillag karakterekkel megjelenteti a mellékátlót (jobb felső sarokból a bal alsóba menőt). Jelölje meg azokat a sorokat, amelyeket a /* ? */ helyére helyettesítve helyes működést kapunk! #include <stdio.h> void main(void) { int i,j; for (i=0;i<21;i++) { for (/* ? */) printf(" "); printf("*\n"); } }
| j=0;j<21-i;j++
| j=0;j<20-i;j++
| j=21;j>i;j--
| j=0;j<i;j++
| 8. Írjon olyan programot, amely a karakteres üzemmódú képernyő egy 21x21-es ablakában csillag karakterekkel megjelenteti a fő- és mellékátlót egyszerre, vagyis egy X-et!
    #include <stdio.h>
    #define MERET 20 /* Az utolso meg ervenyes keppozicio. */
    void main(void) {
      int i,j;
      for (i=0;i<=MERET;i++) {
        for (j=0;j<=MERET;j++)
          if (j==i || j==MERET-i) printf("*"); else printf(" ");
        printf("\n");
      }
    }